using System;
using System.Collections.Generic;
using System.Text;
using Mud.Server.Physical;
using Mud.Common.Messages;
using Mud.Common;

namespace Mud.Server.Client.Commands
{
    /// <summary>
    /// This is the command which is executed by command controler. 
    /// </summary>
    public abstract class ControlerCommand : CommandBase
    {
        #region CommandBase Members

		protected override IMessage ExecuteCommand(IServer server, IMessage commandData, IClient executor)
		{
			return Execute(server, commandData, executor.CreatureControler);
		}

        #endregion

        public IMessage Execute(IServer server, IMessage commandData, CreatureControler controler)
        {
            _server = server;
            _controler = controler;
            _commandData = commandData;
            _timeCommandCreated = _server.CurrentTimeIndex;

            // check if we can even be executed.
            IMessage response = CanExecute();
            if (MessageResponseOk.Instance != response)
                return response;

            // check if we are assigned to an object
            Creature controled = CommandControler.ControledCreature;
            if (controled == null)
                return new MessageResponseError("You are not controling any object.");

            return controled.CommandManager.QueueCommand(this);

        }

        #region Properties

        /// <summary>
        /// Server where the command is executing.
        /// </summary>
        public IServer Server
        {
            get { return _server; }
        }
        private IServer _server;

        /// <summary>
        /// Message containing data for the command.
        /// </summary>
        public IMessage CommandData
        {
            get { return _commandData; }
        }
        private IMessage _commandData;

        /// <summary>
        /// Command controler.
        /// </summary>
        public CreatureControler CommandControler
        {
            get { return _controler; }
            set { _controler = value; }
        }
        private CreatureControler _controler;


        /// <summary>
        /// Duration of the command.
        /// </summary>
        public virtual int Duration
        {
            get { return _duration; }
            protected set { _duration = value; }
        }
        private int _duration;

        /// <summary>
        /// Command cooldown means that the command will not
        /// be ended immediately, but when a certain period of time
        /// elapses.
        /// </summary>
        protected virtual int Cooldown
        {
            get { return _cooldown; }
            set { _cooldown = value; }
        }
        private int _cooldown;


        /// <summary>
        /// Gets the time when the command was created.
        /// </summary>
        public int TimeCommandCreated
        {
            get { return _timeCommandCreated; }
        }
        private int _timeCommandCreated;

        /// <summary>
        /// Gets the time when the command started.
        /// </summary>
        public int TimeCommandStarted
        {
            get { return _timeCommandStarted; }
        }
        private int _timeCommandStarted;

        /// <summary>
        /// Gets the time when the command ended.
        /// </summary>
        public int TimeCommandEnded
        {
            get { return _timeCommandEnded; }
        }
        private int _timeCommandEnded;


        /// <summary>
        /// Gets or sets wether this command is created automaticaly or by client interaction.
        /// Automaticaly created interuptable commands should be interupted if the new 
        /// command is not automatic.
        /// </summary>
        public int IsAutomated
        {
            get { return _isAutomated; }
            set { _isAutomated = value; }
        }
        private int _isAutomated;


        /// <summary>
        /// Type of the command.
        /// </summary>
        public abstract ControlerCommandType Type
        {
            get;
        }

        /// <summary>
        /// Importance of the command (in relation to other commands).
        /// </summary>
        public abstract ControlerCommandImportance Importance
        {
            get;
        }

        /// <summary>
        /// True if the command is currently executing.
        /// </summary>
        public bool Executing
        {
            get { return _executing; }
            protected set { _executing = value; }
        }
        private bool _executing = true;

        #endregion

        #region events

        public delegate void CommandEndedHandler(ControlerCommand command);

        /// <summary>
        /// Event raised when the command is no longer executing.
        /// </summary>
        public event CommandEndedHandler CommandEnded;

        #endregion

        #region Public methods

        /// <summary>
        /// Starts the command.
        /// </summary>
        public virtual IMessage StartCommand()
        {
            _timeCommandStarted = _server.CurrentTimeIndex;
            _executing = true;
            // if we are not started at the moment that we were created then we should
            // again check if we can execute
            if (_timeCommandStarted != _timeCommandCreated)
            {
                IMessage response = CanExecute();
                if (MessageResponseOk.Instance != response)
                {
                    Logger.Log(LogType.Debug, "ControlerCommand.StartCommand", "Command {0} is starting with delay but it can not be executed. Reason {1}.", this, response);
                    EndCommand();
                    return response;
                }
            }
            return StartCommandImpl();
        }

        /// <summary>
        /// Commands with duration, or iterative commands end at some point.
        /// After they are finished this method should be called.
        /// </summary>
        public virtual void EndCommand()
        {
            _timeCommandEnded = _server.CurrentTimeIndex;
            _executing = false;
            Logger.Log(LogType.ExtraDebug, "ControlerCommand.EndCommand", "Command {0} has ended.", this);

            Creature controled = CommandControler.ControledCreature;
            if (controled == null)
            {
                Logger.Log(LogType.Warning, "ControlerCommand.EndCommand", "Command {0} has ended but there is no longer a creature attached to the controler.", this);
                return;
            }
            if (controled.CommandManager.CurrentCommand != this)
            {
                Logger.Log(LogType.Error, "ControlerCommand.EndCommand", "Command {0} has ended but it is no longer the current command for the creature {1}.", this, controled);
                return;
            }
            if (CommandEnded != null)
                CommandEnded(this);
        }


        /// <summary>
        /// Interupts the command due to a specific event.
        /// </summary>
        /// <param name="e">The event which interupted the command.</param>
        public virtual void Interupt(PhysicalEvent e)
        {
            InteruptImpl(e);
            if (Cooldown > 0)
				Server.CurrentScheduler.ScheduleMethod(Server.CurrentTimeIndex + Cooldown, EndCommand);
            else
                EndCommand();
        }

        /// <summary>
        /// Interupts the command due to a specific command.
        /// </summary>
        /// <param name="e">The command which interupted this command.</param>
        public virtual void Interupt(ControlerCommand c)
        {
            InteruptImpl(c);
            if (Cooldown > 0)
				Server.CurrentScheduler.ScheduleMethod(Server.CurrentTimeIndex + Cooldown, EndCommand);
            else
                EndCommand();
        }

        #endregion

        #region Abstract methods

        /// <summary>
        /// Checks if the command can be executed (sanity checking).
        /// </summary>
        public abstract IMessage CanExecute();

        /// <summary>
        /// Determines whether this instance [can be interupted by] the specified event.
        /// </summary>
        /// <param name="e">The e.</param>
        /// <returns>
        /// 	<c>true</c> if this instance [can be interupted by] the specified event; otherwise, <c>false</c>.
        /// </returns>
        public abstract bool CanBeInteruptedBy(PhysicalEvent e);

        /// <summary>
        /// Determines whether this instance [can be interupted by] the specified ControlerCommand.
        /// </summary>
        /// <param name="c">The c.</param>
        /// <returns>
        /// 	<c>true</c> if this instance [can be interupted by] the specified ControlerCommand; 
        /// otherwise, <c>false</c>.
        /// </returns>
        public abstract bool CanBeInteruptedBy(ControlerCommand c);

        /// <summary>
        /// This is where the actual starting of the command happens.
        /// </summary>
        protected abstract IMessage StartCommandImpl();

        /// <summary>
        /// This is where the actual interuption occurs.
        /// </summary>
        protected abstract void InteruptImpl(ControlerCommand c);

        /// <summary>
        /// This is where the actual interuption occurs.
        /// </summary>
        protected abstract void InteruptImpl(PhysicalEvent c);


        #endregion
 
        #region Helper methods

        /// <summary>
        /// Converts the message to other mesage. It throws an error if the message is not the expected
        /// type.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>Null if the message can not be converted or the converted message.</returns>
        protected T AssertIsMessage<T>(IMessage message) where T : class, IMessage
        {
            return AssertIsMessage<T>(message, this);
        }

        #endregion


        public override string ToString()
        {
            if (_controler.ControledCreature != null)
                return this.GetType().Name + "<" + _controler.ControledCreature.Name + ">" + " [" + this.TimeCommandStarted.ToString() + "]";
            else
                return this.GetType().Name + " [" + this.TimeCommandStarted.ToString() + "]";
        }

    }

    /// <summary>
    /// Instant command.
    /// </summary>
    public abstract class InstantControlerCommand : ControlerCommand
    {
        #region Overrides

        public override ControlerCommandType Type
        {
            get { return ControlerCommandType.Instant; }
        }

        public override ControlerCommandImportance Importance
        {
            get { return ControlerCommandImportance.Low; }
        }

        public override int Duration
        {
            get { return 0; }
            protected set { throw new InvalidOperationException("Instant commands always have duration 0."); }
        }

        public override void EndCommand()
        {
            throw new InvalidOperationException("Instant commands can not be ended.");
        }

        protected override void InteruptImpl(ControlerCommand c)
        {
            throw new InvalidOperationException("Instant commands can not be interupted.");
        }

        protected override void InteruptImpl(PhysicalEvent c)
        {
            throw new InvalidOperationException("Instant commands can not be interupted.");
        }

        public override bool CanBeInteruptedBy(ControlerCommand c)
        {
            return false;
        }

        public override bool CanBeInteruptedBy(PhysicalEvent e)
        {
            return false;
        }

        #endregion
    }

    public abstract class InteruptableCommand : ControlerCommand
    {

        #region Overridables


        public override ControlerCommandImportance Importance
        {
            get { return ControlerCommandImportance.Low; }
        }

        public override bool CanBeInteruptedBy(PhysicalEvent e)
        {
            return false; // no event can interupt me
        }

        public override bool CanBeInteruptedBy(ControlerCommand c)
        {
            if (c.Importance >= this.Importance)
                return true;
            return false;
        }

        #endregion
 
    }

    public enum ControlerCommandType
    {
        Instant,
        Normal,
        Iterative
    }

    public enum ControlerCommandImportance
    {
        Low,
        Normal,
        High
    }

}
